/**
 *******************************************************************************
 * @file      lcd_driver.c
 * @version   V1.0.0    
 * @date      2013.05.20
 * @brief     LCD driver for Tananis.	
 *            Resources: 
 *
 * @author    - Adela 
 *            - Robert Zhang <armner@gmail.com>
 *            - 
 */

#include "lcd_driver.h"

#include "stm32f2xx.h"

#define GPIO_PIN_A0 (GPIO_Pin_13)
#define GPIO_PIN_NCS    (GPIO_Pin_14)
#define GPIO_PIN_CLK    (GPIO_Pin_11)
#define GPIO_PIN_MOSI   (GPIO_Pin_10)
#define GPIO_PIN_NRST    (GPIO_Pin_12)

#define A0_HIGH()   do{GPIOD->BSRRL =GPIO_PIN_A0;}while(0)
#define A0_LOW()    do{GPIOD->BSRRH =GPIO_PIN_A0;}while(0)
#define NCS_HIGH()  do{GPIOD->BSRRL =GPIO_PIN_NCS;}while(0)
#define NCS_LOW()   do{GPIOD->BSRRH =GPIO_PIN_NCS;}while(0)
#define MOSI_HIGH() do{GPIOD->BSRRL =GPIO_PIN_MOSI;}while(0)
#define MOSI_LOW()  do{GPIOD->BSRRH =GPIO_PIN_MOSI;}while(0)
#define CLK_HIGH()  do{GPIOD->BSRRL =GPIO_PIN_CLK;}while(0)
#define CLK_LOW()   do{GPIOD->BSRRH =GPIO_PIN_CLK;}while(0)
#define NRST_HIGH()  do{GPIOD->BSRRL =GPIO_PIN_NRST;}while(0)
#define NRST_LOW()   do{GPIOD->BSRRH =GPIO_PIN_NRST;}while(0)


static void u01delay(int x)
{
    while(x--){asm("nop\n");}
}

void spiCmd(u8 Command_Byte)
{
    int i=8; 
    A0_LOW();
    CLK_HIGH();

    u01delay(10);

    NCS_LOW();  
 
    while (i--) {

      CLK_LOW();
      if (Command_Byte&0x80)
        MOSI_HIGH();
      else
        MOSI_LOW();

      Command_Byte <<= 1;

      CLK_HIGH();
      //asm("NOP\n");
    }

    NCS_HIGH();  
    //A0_HIGH();
}

void spiData(char *pData, unsigned char len)
{
    int j;
    CLK_HIGH();
    A0_HIGH();
    u01delay(10);
    NCS_LOW();
    
    for(j=0; j<len; j++)
    {
        char data;
        int i=8;
        data =*pData++;
        while (i--) {
            CLK_LOW();
            if (data&0x80)
                MOSI_HIGH();
            else
                MOSI_LOW();
            data <<= 1;
            CLK_HIGH();
        }
        //asm("nop\n");
    }
    NCS_HIGH();
    //A0_HIGH();  
}

#define CMD_LCD_COL_ADDR_LSB    (0x0)
#define CMD_LCD_COL_ADDR_MSB    (0x10)
#define CMD_LCD_POWER_CTRL  (0x28)
#define CMD_LCD_SCR_LINE_LSB    (0x40)
#define CMD_LCD_SCR_LINE_MSB    (0x50)
#define CMD_LCD_ROW_ADDR_LSB    (0x60)
#define CMD_LCD_ROW_ADDR_MSB    (0x70)
#define CMD_LCD_VBAIS   (0x81)
#define CMD_LCD_DISP_ENBLE  (0xAF)

int lcd_open(Driver *driver)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

    GPIOD->BSRRL =GPIO_PIN_CLK | GPIO_PIN_NCS| GPIO_PIN_A0;
    GPIOD->BSRRH =GPIO_PIN_MOSI | GPIO_PIN_NRST;
    GPIO_InitStructure.GPIO_Pin = GPIO_PIN_A0 | GPIO_PIN_NCS | GPIO_PIN_CLK | GPIO_PIN_MOSI | GPIO_PIN_NRST;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    
    u01delay(200000);
    
    //NRST_LOW();
    
    spiCmd(0x25);   //Temperature compensation curve definition: 0x25 = -0.05%/oC
    spiCmd(0x2b);   //Panel loading set ,Internal VLCD.
    spiCmd(0xEA);	//set bias=1/10 :Command table NO.27
    spiCmd(0x81);	//Set Vop
    spiCmd(50);		//0--255
    spiCmd(0xA6);	//inverse display off
    spiCmd(0xD1);	//SET RGB:Command table NO.21 .SET RGB or BGR.  D1=RGB
    spiCmd(0xD5);	//set color mode 4K and 12bits  :Command table NO.22
    spiCmd(0xA0);	//line rates,25.2 Klps
    spiCmd(0xC8);	//SET N-LINE INVERSION
    spiCmd(0x00);	//Disable NIV
    spiCmd(0xF1);	//Set CEN
    spiCmd(0x3F);	// 1/64DUTY
    spiCmd(0x84);	//Disable Partial Display
    spiCmd(0xC4);	//MY=1,MX=0
    spiCmd(0x89);	//WA=1,column (CA) increment (+1) first until CA reaches CA boundary, then RA will increment by (+1).

    spiCmd(0xF8);	//Set Window Program Enable  ,inside modle
    spiCmd(0xF4);   //starting column address of RAM program window.
    spiCmd(0x00);
    spiCmd(0xF5);   //starting row address of RAM program window.
    spiCmd(0x60);
    spiCmd(0xF6);   //ending column address of RAM program window.
    spiCmd(0x47);
    spiCmd(0xF7);   //ending row address of RAM program window.
    spiCmd(0x9F);

    spiCmd(0xAF);	//dc2=1, IC into exit SLEEP MODE, dc3=1 gray=ON, dc4=1 Green Enhanc mode disabled
  
    return 0;
}



int lcd_write(Driver *driver, void *buffer, int len, OFFSET offset)
{
    spiData(buffer, len);

    return len;
}

int lcd_read(Driver *driver, void *buffer, int len, OFFSET offset)
{
    return 0;
}

int lcd_io_control(Driver *driver, int io_type, int data)
{
    char t_char;
    switch(io_type)
    {
    case IO_CONTROL_LCD_RST:
        break;
        
        //jump over X*3 points
    case IO_CONTROL_LCD_SET_X:
        spiCmd(CMD_LCD_COL_ADDR_LSB | (data &0x0F));
        spiCmd(CMD_LCD_COL_ADDR_MSB | ((data &0x70)>>4) );
        break;
    case IO_CONTROL_LCD_SET_Y:
        spiCmd(CMD_LCD_ROW_ADDR_LSB | (data &0x0F) );
        spiCmd(CMD_LCD_ROW_ADDR_MSB | ((data &0xF0)>>4) );
        break;
    case IO_CONTROL_LCD_SET_XY:
        t_char =data >>8;
        spiCmd(CMD_LCD_COL_ADDR_LSB | (data &0x0F));
        spiCmd(CMD_LCD_COL_ADDR_MSB | ((data &0x70)>>4) );
        spiCmd(CMD_LCD_ROW_ADDR_LSB | (t_char &0x0F) );
        spiCmd(CMD_LCD_ROW_ADDR_MSB | ((t_char &0xF0)>>4) );
        break;
    case IO_CONTROL_LCD_SET_CONTRAST:
        break;
    }
    
    return 0;
}

int lcd_close(Driver *driver)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIOD->BSRRL =0;
    GPIOD->BSRRH =0;
    GPIO_InitStructure.GPIO_Pin = GPIO_PIN_A0 | GPIO_PIN_NCS | GPIO_PIN_CLK | GPIO_PIN_MOSI | GPIO_PIN_NRST;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    return 0;
}

Driver lcd_driver=
{
    &lcd_open,
    &lcd_write,
    &lcd_read,
    &lcd_io_control,
    &lcd_close
};


